package zjy.android.radapter.converter.factory;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.google.gson.Gson;

import java.lang.annotation.Annotation;
import java.lang.reflect.Type;

import okhttp3.ResponseBody;
import retrofit2.Converter;
import retrofit2.Retrofit;
import zjy.android.radapter.annotation.IgnoreData;
import zjy.android.radapter.annotation.ReturnSource;
import zjy.android.radapter.bean.EmptyBean;
import zjy.android.radapter.converter.ReturnConverter;

public final class ReturnConverterFactory extends Converter.Factory {

    private final Gson gson;
    private final Class<?> returnSource;
    private final boolean useAll;

    private ReturnConverterFactory(Gson gson, Class<?> rClass, boolean useAll) {
        this.gson = gson;
        this.returnSource = rClass;
        this.useAll = useAll;
    }

    public static ReturnConverterFactory create() {
        return create(new Gson(), EmptyBean.class, false);
    }

    public static ReturnConverterFactory create(Gson gson, Class<?> returnSource, boolean useAll) {
        return new ReturnConverterFactory(gson, returnSource, useAll);
    }

    public static ReturnConverterFactory create(Class<?> returnSource, boolean useAll) {
        return new ReturnConverterFactory(new Gson(), returnSource, useAll);
    }

    public static ReturnConverterFactory create(Gson gson) {
        return new ReturnConverterFactory(gson, EmptyBean.class, false);
    }

    @Nullable
    @Override
    public Converter<ResponseBody, ?> responseBodyConverter(@NonNull Type type,
                                                            @NonNull Annotation[] annotations,
                                                            @NonNull Retrofit retrofit) {
        Class<?> tempSource;
        if (checkIgnore(annotations)) {//优先判断是否忽略
//            return super.responseBodyConverter(type, annotations, retrofit);
        } else if ((tempSource = getAppointSource(annotations)) != null) {//判断是否是指定的结果类
            return createConverter(type, tempSource);
        } else if (checkUseAll()) {//判断是否所有都使用
            return createConverter(type, returnSource);
        } else if (checkReturnSource(annotations)) {//判断当前是否使用
            return createConverter(type, returnSource);
        }
        return super.responseBodyConverter(type, annotations, retrofit);
    }

    private boolean checkReturnSource(Annotation[] annotations) {
        return getAnnotation(annotations, ReturnSource.class) != null && checkNotEmpty(returnSource);
    }

    private boolean checkNotEmpty(Class<?> target) {
        return !EmptyBean.class.isAssignableFrom(target);
    }

    /**
     * 获取指定注解
     *
     * @param annotations 注解数组，用于判断是否有指定的注解
     * @param aClass      指定注解的Class
     * @param <T>         指定注解的类型
     * @return 返回指定注解，不存在返回null
     */
    @SuppressWarnings("unchecked")
    private <T> T getAnnotation(Annotation[] annotations, Class<T> aClass) {
        for (Annotation ann : annotations) {
            if (aClass.isAssignableFrom(ann.getClass())) return (T) ann;
        }
        return null;
    }

    /**
     * 获取指定结果类
     *
     * @param annotations 注解数组，用于判断是否存在指定注解
     * @return 结果类
     */
    private Class<?> getAppointSource(Annotation[] annotations) {
        ReturnSource rsAnn = getAnnotation(annotations, ReturnSource.class);
        if (rsAnn != null && checkNotEmpty(rsAnn.value())) {
            return rsAnn.value();
        }
        return null;
    }

    /**
     * 检测是否忽略
     *
     * @param annotations 注解数组，用于判断是否存在忽略注解
     * @return true/false
     */
    private boolean checkIgnore(Annotation[] annotations) {
        return getAnnotation(annotations, IgnoreData.class) != null;
    }

    /**
     * 检测是否所有都使用
     *
     * @return true/false
     */
    private boolean checkUseAll() {
        return useAll && checkNotEmpty(returnSource);
    }

    /**
     * 创建转换器
     *
     * @param type         需要返回的类型
     * @param returnSource 需要解析的类型
     * @return 转换器
     */
    private Converter<ResponseBody, ?> createConverter(Type type, Class<?> returnSource) {
        return new ReturnConverter(gson, type, returnSource);
    }
}
